#include "fraction.h"

int Fraction::gcd(int a, int b)
{
    while (b > 0)
    {
        int c = a % b;
        a = b;
        b = c;
    }
    return a;
}

std::string Fraction::toString()
{
    std::string result;
    if (numerator == 0)
        return "0";
    if (numerator < 0)
    {
        result += "-";
    }
    int quotient = abs(numerator) / denominator;
    int remainder = abs(numerator) % denominator;
    if (quotient == 0)
    {
        result += std::to_string(remainder) + "/" + std::to_string(denominator);
    }
    else
    {
        result += std::to_string(quotient) + " ";
        if (remainder != 0)
        {
            int gcd_value = gcd(remainder, denominator);
            remainder /= gcd_value;
            denominator /= gcd_value;
            result += std::to_string(remainder) + "/" + std::to_string(denominator);
        }
    }
    if (result.back() == '/')
    {
        result.pop_back();
        result.pop_back();
    }
    return result;
}

int Fraction::lcm(int a, int b)
{
    return gcd(a, b) * a * b;
}

void Fraction::reduce()
{
    int g = gcd(abs(numerator), denominator);
    if (g != 1)
    {
        numerator = numerator / g;
        denominator = denominator / g;
    }
}

Fraction::Fraction()
{
    numerator = 0;
    denominator = 1;
}

Fraction::Fraction(int numerator, int denominator)
{
    if (denominator == 0)
    {
        std::cout << "Error" << std::endl;
    }
    this->numerator = numerator;
    this->denominator = denominator;
    reduce();
}

Fraction::Fraction(const Fraction &fr)
{
    numerator = fr.numerator;
    denominator = fr.denominator;
    reduce();
}

Fraction::Fraction(const std::string &str)
{
    std::string s = str;
    int whole_number = 0, num = 0, den = 1;
    size_t space_pos = s.find(' ');
    size_t slash_pos = s.find('/');
    if (space_pos != std::string::npos)
    {
        whole_number = std::stoi(s.substr(0, space_pos));
        s = s.substr(space_pos + 1);
        slash_pos = s.find('/');
    }
    if (slash_pos != std::string::npos)
    {
        num = std::stoi(s.substr(0, slash_pos));
        den = std::stoi(s.substr(slash_pos + 1));
    }
    else
    {
        num = std::stoi(s);
    }
    int sgn;
    if (whole_number >= 0)
        sgn = 1;
    else
        sgn = -1;
    numerator = whole_number * den + num * sgn;
    denominator = den;
    reduce();
}

Fraction::Fraction(const double &df)
{
    double integral = std::floor(df);
    double frac = df - integral;

    const int precision = 1000000000; // This is the accuracy.

    int gcd_ = gcd(std::round(frac * precision), precision);

    denominator = precision / gcd_;
    numerator = std::round(frac * precision) / gcd_ + integral * denominator;
    reduce();
}

Fraction::Fraction(const char *ch)
{
    std::string s(ch);
    int whole_number = 0, num = 0, den = 1;
    size_t space_pos = s.find(' ');
    size_t slash_pos = s.find('/');
    if (space_pos != std::string::npos)
    {
        whole_number = std::stoi(s.substr(0, space_pos));
        s = s.substr(space_pos + 1);
        slash_pos = s.find('/');
    }
    if (slash_pos != std::string::npos)
    {
        num = std::stoi(s.substr(0, slash_pos));
        den = std::stoi(s.substr(slash_pos + 1));
    }
    else
    {
        num = std::stoi(s);
    }
    int sgn;
    if (whole_number >= 0)
        sgn = 1;
    else
        sgn = -1;
    numerator = whole_number * den + num * sgn;
    denominator = den;
    reduce();
}

Fraction &Fraction::operator=(const Fraction &fraction)
{
    this->numerator = fraction.numerator;
    this->denominator = fraction.denominator;
    reduce();
    return *this;
}

Fraction &Fraction::operator=(const double &df)
{
    double integral = std::floor(df);
    double frac = df - integral;

    const int precision = 1000000000; // This is the accuracy.

    int gcd_ = gcd(std::round(frac * precision), precision);

    this->denominator = precision / gcd_;
    this->numerator = std::round(frac * precision) / gcd_ + integral * denominator;
    reduce();
    return *this;
}

Fraction &Fraction::operator=(const std::string &str)
{
    std::string s(str);
    int whole_number = 0, num = 0, den = 1;
    size_t space_pos = s.find(' ');
    size_t slash_pos = s.find('/');
    if (space_pos != std::string::npos)
    {
        whole_number = std::stoi(s.substr(0, space_pos));
        s = s.substr(space_pos + 1);
        slash_pos = s.find('/');
    }
    if (slash_pos != std::string::npos)
    {
        num = std::stoi(s.substr(0, slash_pos));
        den = std::stoi(s.substr(slash_pos + 1));
    }
    else
    {
        num = std::stoi(s);
    }
    int sgn;
    if (whole_number >= 0)
        sgn = 1;
    else
        sgn = -1;
    numerator = whole_number * den + num * sgn;
    denominator = den;
    reduce();
    return *this;
}

Fraction Fraction::operator+(const Fraction &fraction)
{
    int den = gcd(this->denominator, fraction.denominator);

    den = (this->denominator * fraction.denominator) / den;
    int num = (this->numerator) * (den / this->denominator) + (fraction.numerator) * (den / fraction.denominator);
    Fraction f(num, den);
    return f;
}

Fraction Fraction::operator-(const Fraction &fraction)
{
    int new_numerator = numerator * fraction.denominator - fraction.numerator * denominator;
    int new_denominator = denominator * fraction.denominator;
    Fraction f(new_numerator, new_denominator);
    return f;
}

Fraction Fraction::operator*(const Fraction &fraction)
{
    int new_numerator = numerator * fraction.numerator;
    int new_denominator = denominator * fraction.numerator;
    Fraction f(new_numerator, new_denominator);
    return f;
}

Fraction Fraction::operator/(const Fraction &fraction)
{
    int new_numerator = numerator * fraction.denominator;
    int new_denominator = denominator * fraction.numerator;
    Fraction f(new_numerator, new_denominator);
    return f;
}

Fraction Fraction::operator+=(const Fraction &fr)
{
    this->numerator += fr.numerator;
    this->denominator += fr.denominator;
    reduce();
    return *this;
}

Fraction Fraction::operator+=(const double &db)
{
    // Преобразование десятичной дроби в дробь
    Fraction fraction(db * denominator, denominator);

    // Сложение текущей дроби с полученной дробью
    *this = *this + fraction;

    // Возврат ссылки на текущий объект
    return *this;
}

std::ostream &operator<<(std::ostream &os, Fraction &fr)
{
    os << fr.toString();
    return os;
}

Fraction &Fraction::operator=(const char *ch)
{
    std::string s(ch);
    int whole_number = 0, num = 0, den = 1;
    size_t space_pos = s.find(' ');
    size_t slash_pos = s.find('/');
    if (space_pos != std::string::npos)
    {
        whole_number = std::stoi(s.substr(0, space_pos));
        s = s.substr(space_pos + 1);
        slash_pos = s.find('/');
    }
    if (slash_pos != std::string::npos)
    {
        num = std::stoi(s.substr(0, slash_pos));
        den = std::stoi(s.substr(slash_pos + 1));
    }
    else
    {
        num = std::stoi(s);
    }
    int sgn;
    if (whole_number >= 0)
        sgn = 1;
    else
        sgn = -1;
    numerator = whole_number * den + num * sgn;
    denominator = den;
    return *this;
}

std::istream &operator>>(std::istream &is, Fraction &fr)
{
    std::string value;
    getline(std::cin, value);
    fr = Fraction(value);
    return is;
}

Fraction Fraction::operator+(const double &fraction)
{
    Fraction fr(fraction);
    return *this + fr;
}

Fraction Fraction::operator+(const int &fraction)
{
    Fraction fr(fraction);
    return *this + fr;
}

Fraction operator+(const int &i, const Fraction &fr)
{
    Fraction nFr(i);
    return nFr + fr;
}

Fraction operator+(const double &d, const Fraction &fr)
{
    Fraction nFr(d);
    return nFr + fr;
}

Fraction operator-(const int &i, const Fraction &fr)
{
    Fraction nFr(i);
    return nFr - fr;
}

Fraction operator-(const double &d, const Fraction &fr)
{
    Fraction nFr(d);
    return nFr - fr;
}

Fraction operator/(const int &i, const Fraction &fr)
{
    Fraction nFr(i);
    return nFr / fr;
}

Fraction operator/(const double &d, const Fraction &fr)
{
    Fraction nFr(d);
    return nFr / fr;
}

Fraction operator*(const int &i, const Fraction &fr)
{
    Fraction nFr(i);
    return nFr * fr;
}

Fraction operator*(const double &d, const Fraction &fr)
{
    Fraction nFr(d);
    return nFr * fr;
}
